<?php

namespace Api;

use Validator;
use Illuminate\Support\Str;
use Illuminate\Support\Arr;

/*
|--------------------------------------------------------------------------
| 特性：模型拓展
|--------------------------------------------------------------------------
| 数据校验并填充
|--------------------------------------------------------------------------
| 扩展强制字段类型
|--------------------------------------------------------------------------
|
*/

trait ModelExtTrait
{
    /**
     * 数据校验规则
     * @var array
     */
    // private $rules = [];

    /**
     * 启动特性
     *
     * @return void
     */
    public static function bootModelExtTrait()
    {
        static::saving(function ($model) {
            // 获取外部输入的数据（未执行过自定义调整器的数据）
            $inputAttributes = $model->getDirty();

            // 更新
            if ($model->getKey()) {
                // 仅针对脏数据进行校验
                $modelRules = array_intersect_key($model->rules, $inputAttributes);
            }
            // 创建
            else {
                // 针对所有数据进行校验
                $modelRules = $model->rules;
            }

            // 构造
            $rules    = [];
            $messages = [];
            $extendMethodArr = [];
            foreach ($modelRules as $key => $value) {
                // 扩展自定义验证方法
                if (isset($value['methods'])) {
                    $extendMethodArr[$key] = explode(',', $value['methods']);
                    unset($value['methods']);
                }
                
                // 构造验证规则
                $rules[$key] = implode('|', array_keys($value));
                
                // 构造错误信息
                array_walk($value, function ($v, $k) use ($key, &$messages) {
                    $k = explode(':', $k);
                    $messages[$key.'.'.$k[0]] = $v;
                });
            }
            
            // 对请求的数据进行校验
            $validator = Validator::make($inputAttributes, $rules, $messages);
            $validator->original = $model->getOriginal(); // 更新操作时，转存查询出的原始数据
            if ($validator->fails()) {
                error('validation_fail', $validator->errors()->toArray());
            }

            // 扩展自定义验证方法
            foreach ($extendMethodArr as $key => $methods) {
                foreach ($methods as $method) {
                    $result = $model->$method($inputAttributes[$key]);
                    if ($result === false) {
                        error('validation_fail', [$key => 'fail']);
                    }
                    else if (is_string($result)) {
                        error('validation_fail', [$key => [$result]]);
                    }
                }
            }

            // 调用自定义调整器
            foreach ($inputAttributes as $key => $value) {
                if ($model->hasSetMutator($key)) {
                    $method = 'set'.Str::studly($key).'Attribute';
                    $model->{$method}($value);
                }
            }
        });
    }

    /**
     * Set a given attribute on the model.
     *
     * @param  string  $key
     * @param  mixed  $value
     * @return $this
     */
    public function setAttribute($key, $value)
    {
        if ($value && $this->isDateAttribute($key)) {
            $value = $this->fromDateTime($value);
        }

        if ($this->isJsonCastable($key) && ! is_null($value)) {
            $value = $this->castAttributeAsJson($key, $value);
        }

        // If this attribute contains a JSON ->, we'll set the proper value in the
        // attribute's underlying array. This takes care of properly nesting an
        // attribute in the array's value in the case of deeply nested items.
        if (Str::contains($key, '->')) {
            return $this->fillJsonAttribute($key, $value);
        }

        $this->attributes[$key] = $value;

        return $this;
    }

    /**
     * 扩展强制字段类型
     * Cast an attribute to a native PHP type.
     * 支持的格式：int, float, string, boolean, object, array, collection, date, datetime, timestamp
     *
     * @param  string  $key
     * @param  mixed  $value
     * @return mixed
     */
    protected function castAttribute($key, $value)
    {
        $value = parent::castAttribute($key, $value);
        if (is_null($value)) {
            switch ($this->getCastType($key)) {
                case 'object':
                    $value = (object) null;
                    break;
                case 'array':
                    $value = [];
                    break;
            }
        }
        return $value;
    }

    /**
     * 获取指定字段的原始值
     *
     * @param array $columns
     * @return array
     */
    public function getOriginals(array $columns)
    {
        $attributes = [];
        foreach (Arr::only($this->original, $columns) as $key => $value) {
            $attributes['original_'.$key] = $value;
        }

        return $attributes;
    }

    /**
     * 合并输出原始字段值
     *
     * @param  array|string  $attributes
     * @return $this
     */
    public function mergeOriginals($attributes)
    {
        $attributes = is_string($attributes) ? func_get_args() : $attributes;
        $original   = Arr::only($this->original, $attributes);

        foreach ($original as $key => $value) {
            $this->visible[] = 'original_'.$key;
            $this->attributes['original_'.$key] = $value;
        }

        return $this;
    }


}
